package ru.ifmo.automaton;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author avhaliullin
 */
public class AutomatonImp<C, S> implements Automaton<C, S> {
    private final Map<S, Map<C, S>> transitions;
    private final Set<S> terminalStates;
    private final S initialState;
    private final S failState;
    private final List<C> alphabet;
    private S currentState;

    public AutomatonImp(Map<S, Map<C, S>> transitions, Set<S> terminalStates, S initialState, S failState, List<C> alphabet) {
        this.transitions = transitions;
        this.terminalStates = terminalStates;
        this.initialState = initialState;
        this.failState = failState;

        currentState = initialState;
        this.alphabet = alphabet;
    }

    public S getInitialState() {
        return initialState;
    }

    public int getStatesCount() {
        return transitions.size();
    }

    public void goToState(S state) {
        currentState = state;
    }

    public S getCurrentState() {
        return currentState;
    }

    public int getTerminalStatesCount() {
        return terminalStates.size();
    }

    public boolean isInTerminalState() {
        return terminalStates.contains(currentState);
    }

    private S oneStep(C symbol) {
        if (transitions.get(currentState) == null || transitions.get(currentState).get(symbol) == null) {
            return failState;
        }
        currentState = transitions.get(currentState).get(symbol);
        return currentState;
    }

    public S step(C... word) {
        S res = currentState;
        for (C symbol : word) {
            res = oneStep(symbol);
        }
        return res;
    }

    public Iterator<S> iterator() {
        return transitions.keySet().iterator();
    }

    public List<C> getAlphabet() {
        return alphabet;
    }
}
